package implementacionJ48;

import weka.core.Attribute;
import weka.core.Instance;
import java.util.*;


import weka.core.Instances;

public class Entropy {
	
	/* Calculamos la entropia del conjunto S */
	public static double calculateInfo(Instances S) throws Exception{
	
		/* Aqui traemos la cant. total de atributos(restando la cant. de filas con valor desconocido) */
		int cantFilasTotal = S.attributeStats(0).totalCount;

		Enumeration instancias = S.enumerateInstances();
		
		/* Aqui armamos el vector cantFilasPorClase, que tiene la cantidad de filas por cada clase */
		double[] cantFilasPorClase = new double[S.numClasses()];
		for(int i = 0; i < cantFilasTotal; i++){
			boolean isMissing = false;
			Instance instancia = (Instance)instancias.nextElement();
			
				/* Chequeamos si falta algun valor en toda la fila de la instancia */
				for(int j = 0; j < S.numAttributes() - 1; j++){
					if(instancia.isMissing(j)){
						isMissing = true;
						break;
					}
				}
				
				/* Si falta algun valor, no contamos la instancia en la cantidad de filas por clase */
				if(!isMissing )
					cantFilasPorClase[(int)instancia.classValue()] +=1;
		}
		
		/* Calculamos el nro de filas que tienen al menos un atributo desconocido */
		int missingValuesRows = 0;
		for(int i = 0; i < S.attributeStats(0).totalCount; i++){
			for(int j = 0; j < S.numAttributes(); j++){
				if(S.instance(i).isMissing(j)){
					missingValuesRows++;
					break;
				}
			}
		}
		
		/* Esta variable contiene la cantidad de filas en las que todas sus columnas tienen un valor conocido */
		int cantFilasConValor = S.attributeStats(0).totalCount - missingValuesRows;
		
		double info = 0.0;
		for(int i = 0; i < S.numClasses(); i++){
			double p = (double) cantFilasPorClase[i]/(double)cantFilasConValor;
			/* Aqui chequeamos que p sea != de cero porque Logartimo de 0 no existe */
			if(p != 0)
				info += p*(Math.log10(p)/Math.log10(2));
		}
			
		return -info;
		
	}

	/* Calculamos la entropia del conjunto S */
	public static double calculateInfoAttribute(Instances S, Attribute attr) throws Exception{
	
		/* Aqui traemos la cant. total de atributos(restando la cant. de filas con valor desconocido) */
		int cantFilasTotal = S.attributeStats(0).totalCount;

		Enumeration instancias = S.enumerateInstances();
		
		/* Aqui armamos el vector cantFilasPorClase, que tiene la cantidad de filas por cada clase
		 * no contamos los casos en los que el valor del atributo a evaluar es desconocido */
		double[] cantFilasPorClase = new double[S.numClasses()];
		for(int i = 0; i < cantFilasTotal; i++){
			boolean isMissing = false;
			Instance instancia = (Instance)instancias.nextElement();
			
			if(instancia.isMissing(attr)){
				isMissing = true;
			}

			/* Si falta algun valor, no contamos la instancia en la cantidad de filas por clase */
			if(!isMissing )
				cantFilasPorClase[(int)instancia.classValue()] +=1;
			
		}
		
		/* Esta variable contiene la cantidad de filas en las que todas sus columnas tienen un valor conocido */
		//int cantFilasConValor = cantFilasTotal;
		
		double info = 0.0;
		for(int i = 0; i < S.numClasses(); i++){
			double p = (double) cantFilasPorClase[i]/(double)cantFilasTotal;
			/* Aqui chequeamos que p sea != de cero porque Logartimo de 0 no existe */
			if(p != 0)
				info += p*(Math.log10(p)/Math.log10(2));
		}
			
		return -info;
		
	}	
	
	/* Calculamos la entropia de los conjuntos resultantes */
	public static double calculateInfoX(Instances S, Attribute attr) throws Exception{
		double infoX = 0.0;
		
		/* Calculamos todos los missing values de una columna */
		int missingValues = 0;
		for(int i = 0; i < S.numInstances(); i++){
			if(S.instance(i).isMissing(attr))
				missingValues++;
		}
		
		/* Aqui traemos la cant. total de filas menos la cant. de valores desconocidos */
		int cantFilasConValor = S.numInstances() - missingValues;
		
		int numValoresAtributo = S.numDistinctValues(attr);
		double value = 0.0;
		
		for(int i = 0; i < numValoresAtributo; i++){
			
			Instances Sv = null;
			
			if(missingValues > 0)
				Sv = TestData.selectInstancesWhere_WithUnknown(S, attr, value);
			else
				Sv = TestData.selectInstancesWhere(S, attr, value);
			
			int cardinalidadSv = Sv.numInstances();
			
			double info = calculateInfoAttribute(Sv, attr);
			infoX += ((double)cardinalidadSv/(double)cantFilasConValor)*info;

			/* Siguiente valor posible */
			value += 1.0;
		}
		
		return infoX;
		
	}
	
	public static double calculateF(Instances S, Attribute attr) throws Exception{
		
		/* Calculamos todos los missing values de una columna */
		int missingValues = 0;
		for(int i = 0; i < S.numInstances(); i++){
			if(S.instance(i).isMissing(attr))
				missingValues++;
		}
		double F = (double)(S.numInstances() - missingValues)/S.numInstances();
		
		return F;
		
	}
	
	
}
